package pri.zxx.webdemo.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.StatusLine;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.springframework.core.io.ClassPathResource;

import javax.net.ssl.SSLContext;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.Map;


/**
 * http工具类
 *
 * @author shich
 * @since 2019/12/14
 */
@Slf4j
public class HttpUtil {

    private static final RequestConfig REQUEST_CONFIG;
    private static final PoolingHttpClientConnectionManager CM;
    private static final String EMPTY_STR = "";
    private static final String UTF_8 = "UTF-8";

    static {
        CM = new PoolingHttpClientConnectionManager();
        CM.setMaxTotal(400);
        CM.setDefaultMaxPerRoute(200);
        SocketConfig socketConfig = SocketConfig.custom().setSoTimeout(2000).build();
        CM.setDefaultSocketConfig(socketConfig);

        REQUEST_CONFIG = RequestConfig.custom()
                .setConnectTimeout(2000)
                .setSocketTimeout(2000)
                .setConnectionRequestTimeout(2000)
                .build();
    }

    /**
     * 通过连接池获取HttpClient
     */
    private static CloseableHttpClient getHttpClient() {
        return HttpClients.custom().setConnectionManager(CM).build();
    }

    /**
     * sslHttp
     */
    private static CloseableHttpClient getHttpClient(String keyType, String certPath, String secret) throws Exception {
        return HttpClients.custom()
                .setConnectionManager(CM)
                .setSSLSocketFactory(sslFactory(keyType, certPath, secret))
                .build();
    }

    /**
     * json方式httpPost请求
     */
    public static String httpPost(String url, String jsonParam) {
        try {
            HttpPost method = new HttpPost(url);
            if (StringUtils.isNotBlank(jsonParam)) {
                // 解决中文乱码问题
                StringEntity entity = new StringEntity(jsonParam, "utf-8");
                entity.setContentEncoding("UTF-8");
                entity.setContentType("application/json");
                method.setEntity(entity);
            }
            return getResult(method);
        } catch (Exception e) {
            log.warn("httpPost请求异常：{}", e.getMessage());
            return null;
        }
    }

    /**
     * 普通GET请
     */
    public static String httpGetRequest(String url) {
        HttpGet httpGet = new HttpGet(URI.create(url));
        return getResult(httpGet);
    }

    /**
     * 不设置包头POST传输
     */
    public static String httpGetRequest(String url, Map<String, Object> params) {
        try {
            URIBuilder ub = new URIBuilder();
            ub.setPath(url);

            ArrayList<NameValuePair> pairs = covertParamsToNameValuePairs(params);
            ub.setParameters(pairs);

            HttpGet httpGet = new HttpGet(ub.build());
            return getResult(httpGet);
        } catch (Exception e) {
            return null;
        }
    }

    public static String httpPut(String paramJsonObject, Map<String, Object> headers, String url) {

        HttpPut putMethod;
        try {
            putMethod = new HttpPut(url);//传入URL地址
            //设置请求头
            for (Map.Entry<String, Object> param : headers.entrySet()) {
                putMethod.addHeader(param.getKey(), String.valueOf(param.getValue()));
            }
            putMethod.addHeader("Content-type", "application/json; charset=utf-8");
            putMethod.setEntity(new StringEntity(paramJsonObject, StandardCharsets.UTF_8));
            return getResult(putMethod);
        } catch (Exception e) {
            log.error("put method exception ", e);
            return null;
        }
    }

    /**
     * 设置包头GET传输
     */
    public static String httpGetRequest(String url, Map<String, Object> headers, Map<String, Object> params) {
        try {
            URIBuilder ub = new URIBuilder();
            ub.setPath(url);
            if (null != params) {
                ArrayList<NameValuePair> pairs = covertParamsToNameValuePairs(params);
                ub.setParameters(pairs);
            }
            HttpGet httpGet = new HttpGet(ub.build());
            for (Map.Entry<String, Object> param : headers.entrySet()) {
                httpGet.addHeader(param.getKey(), String.valueOf(param.getValue()));
            }
            return getResult(httpGet);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 不设置包头POST传输
     */
    public static String httpPostRequest(String url, Map<String, Object> params) {
        try {
            HttpPost httpPost = new HttpPost(url);
            ArrayList<NameValuePair> pairs = covertParamsToNameValuePairs(params);
            httpPost.setEntity(new UrlEncodedFormEntity(pairs, UTF_8));
            return getResult(httpPost);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 设置包头POST传输
     */
    public static String httpPostRequest(String url, Map<String, Object> headers, Map<String, Object> params) {
        try {
            HttpPost httpPost = new HttpPost(url);
            for (Map.Entry<String, Object> param : headers.entrySet()) {
                httpPost.addHeader(param.getKey(), String.valueOf(param.getValue()));
            }
            ArrayList<NameValuePair> pairs = covertParamsToNameValuePairs(params);
            httpPost.setEntity(new UrlEncodedFormEntity(pairs, UTF_8));
            return getResult(httpPost);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 设置包头POST传输
     */
    public static String httpPostJsonRequest(String url, Map<String, Object> headers, String jsonParam) {
        try {
            HttpPost httpPost = new HttpPost(url);
            if (null != headers) {
                for (Map.Entry<String, Object> param : headers.entrySet()) {
                    httpPost.addHeader(param.getKey(), String.valueOf(param.getValue()));
                }
            }
            if (StringUtils.isNotBlank(jsonParam)) {
                StringEntity entity = new StringEntity(jsonParam, "utf-8");
                entity.setContentEncoding("UTF-8");
                entity.setContentType("application/json");
                httpPost.setEntity(entity);
            }
            return getResult(httpPost);
        } catch (Exception e) {
            return null;
        }
    }

    private static ArrayList<NameValuePair> covertParamsToNameValuePairs(Map<String, Object> params) {
        ArrayList<NameValuePair> pairs = new ArrayList<NameValuePair>();
        for (Map.Entry<String, Object> param : params.entrySet()) {
            pairs.add(new BasicNameValuePair(param.getKey(), String.valueOf(param.getValue())));
        }

        return pairs;
    }

    /**
     * 返回 status code
     *
     * @param url request 地址
     * @return status
     */
    public static int httpHead(String url, Integer connectTimeout, Integer socketTimeout, Integer connectionRequestTimeout) {
        HttpHead request = new HttpHead(url);
        if (connectTimeout != null && socketTimeout != null && connectionRequestTimeout != null) {
            request.setConfig(RequestConfig.custom().setConnectTimeout(connectionRequestTimeout).setConnectionRequestTimeout(connectionRequestTimeout).setSocketTimeout(connectionRequestTimeout).build());
        } else {
            request.setConfig(REQUEST_CONFIG);
        }
        CloseableHttpClient httpClient = getHttpClient();
        CloseableHttpResponse response = null;
        int statusCode = 9999;
        try {
            response = httpClient.execute(request);
            StatusLine statusLine = response.getStatusLine();
            if (statusLine != null) {
                statusCode = statusLine.getStatusCode();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (response != null) {
                try {
                    response.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return statusCode;
    }

    public static String post(String uri, Map<String, String> param, Object body) {
        URIBuilder builder;
        try {
            builder = new URIBuilder(uri);
        } catch (URISyntaxException e) {
            log.error("http sender,send request by post failed,uri syntax error,uri:{}", uri);
            return null;
        }
        if (param != null) {
            param.forEach((key, value) -> builder.setParameter(key, value));
        }
        HttpPost request;
        try {

            request = new HttpPost(builder.build());
            if (body != null) {
                HttpEntity entity = new StringEntity(GsonUtil.toJson(body), ContentType.APPLICATION_JSON);
                request.setEntity(entity);
            }
        } catch (URISyntaxException e) {
            return null;
        }

        HttpResponse response = null;
        try {
            response = getHttpClient().execute(request);
        } catch (IOException e) {
            log.error("todo ", e);//todo

        }
        if (response == null) {
            return null;
        }

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        try {
            response.getEntity().writeTo(outputStream);
        } catch (IOException e) {
            log.error("", e);

        }
        return new String(outputStream.toByteArray());

    }

    /**
     * 处理Http请求
     */
    private static String getResult(HttpRequestBase request) {
        request.setConfig(RequestConfig.custom().setSocketTimeout(40000).build());
        CloseableHttpClient httpClient = getHttpClient();
        long l = System.currentTimeMillis();
        CloseableHttpResponse response = null;
        try {
            response = httpClient.execute(request);
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                String result = EntityUtils.toString(entity);
                response.close();
                log.info("发送请求耗时" + (System.currentTimeMillis() - l));
                return result;
            }
        } catch (Exception e) {
            log.warn("接口异常，耗时：{}，地址：{}，异常信息：{}", (System.currentTimeMillis() - l), request.getURI(), e.getMessage());
            log.error("执行http异常:{}", e);
            return null;
        } finally {
            try {
                if (null != response) {
                    response.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        long l1 = System.currentTimeMillis();
        log.info("发送请求耗时" + (l1 - l));
        return EMPTY_STR;
    }

    /**
     * 生成ssFactory
     *
     * @param certPath 证书地址
     * @param keyType  keystore 类型
     * @param secret   keystore 密码
     */
    public static SSLConnectionSocketFactory sslFactory(String keyType, String certPath, String secret) throws Exception {
        //证书地址
        //指定读取证书格式为PKCS12
        KeyStore keyStore = KeyStore.getInstance((StringUtils.isBlank(keyType) ? "PKCS12" : keyType));
        //读取本机存放的PKCS12证书文件
        ClassPathResource classPathResource = new ClassPathResource(certPath);
        try (InputStream instream = classPathResource.getInputStream()) {
            //指定PKCS12的密码
            keyStore.load(instream, secret.toCharArray());
        }
        //指定TLS版本
        SSLContext sslcontext = SSLContexts.custom()
                .loadKeyMaterial(keyStore, secret.toCharArray())
                .build();
        //设置httpclient的SSLSocketFactory
        SSLConnectionSocketFactory sslSf = new SSLConnectionSocketFactory(
                sslcontext,
                new String[]{"TLSv1"},
                null,
                SSLConnectionSocketFactory.getDefaultHostnameVerifier());
        return sslSf;
    }

    /**
     * https
     */
    public String sendSslPost(String stringUrl, Map<String, Object> headers, Map<String, Object> params, String secret, String certPath) throws Exception {

        CloseableHttpClient httpClient = getHttpClient(null, certPath, secret);
        try {
            HttpPost httpPost = new HttpPost(URI.create(stringUrl));
            if (headers != null && headers.size() > 0) {
                for (Map.Entry<String, Object> param : headers.entrySet()) {
                    httpPost.addHeader(param.getKey(), String.valueOf(param.getValue()));
                }
            }
            ArrayList<NameValuePair> pairs = covertParamsToNameValuePairs(params);
            httpPost.setEntity(new UrlEncodedFormEntity(pairs, UTF_8));
            httpPost.setConfig(RequestConfig.custom().setSocketTimeout(40000).build());
            long l = System.currentTimeMillis();
            CloseableHttpResponse response = null;
            try {
                response = httpClient.execute(httpPost);
                HttpEntity entity = response.getEntity();
                if (entity != null) {
                    String result = EntityUtils.toString(entity);
                    response.close();
                    log.info("发送请求耗时" + (System.currentTimeMillis() - l));
                    return result;
                }
            } catch (Exception e) {
                log.error("执行请求异常,url:{},执行时间：{}，", stringUrl, (System.currentTimeMillis() - l), e);
                return null;
            } finally {
                try {
                    if (null != response) {
                        response.close();
                    }
                } catch (IOException e) {
                    log.error("关闭流异常", e);
                }
            }
            long l1 = System.currentTimeMillis();
            log.info("发送请求耗时" + (l1 - l));
            return EMPTY_STR;
        } catch (Exception e) {
            return null;
        }

    }
}
